package org.sergiy.metawidget.rules;

import java.net.URL;

import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import org.drools.RuleBase;
import org.drools.WorkingMemory;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.impl.*;
import org.metawidget.inspector.impl.propertystyle.*;
import org.metawidget.inspector.propertytype.*;

/**
 * Metawidget components test
 * 
 * @author Sergiy
 *
 */
@Test
public class MetawidgetTestCase extends BusinessRulesCoreTestCase {
	protected static final String UI_RULES_SRC = "ui-rules.drl";

	/**
	 *	Default constructor 
	 */
	public MetawidgetTestCase() {
	}

	@BeforeClass
	@Override
	protected void init() throws Exception {
		super.init();
	}

	@AfterClass
	@Override
	protected void destroy() throws Exception {
		super.destroy();
	}

	@BeforeMethod
	@Override
	protected void setUp() throws Exception {
		super.setUp();
	}

	@AfterMethod
	@Override
	protected void tearDown() throws Exception {
		super.tearDown();
	}
	
	/**
	 * Return DRL sources
	 * 
	 * @return DRL sources
	 */
	@Override
	protected Object[] getDrlSources() {
		URL rulesSrc = this.getClass().getClassLoader().getResource(UI_RULES_SRC);
		Assert.assertNotNull(rulesSrc, "Unable to get URL for DRL sources");
		// return
		return new Object[] {UI_RULES_SRC};
	}
	
	@DataProvider(name = "validValuesForInspector")
	public Object[][] createValidValuesForInspector() throws Exception {
		return new Object[][] {
				{new DomainObject(false, 10, "main"), DomainObject.class.getName()},
				{new DomainObject(true, 1, "exit"), DomainObject.class.getName()}
		};		
	}
	
	@Test (dataProvider="validValuesForInspector")
	public void testPropertyTypeInspector(Object toInspect, String type) throws Exception {
		// start marker
		logStartTestMarker ("testPropertyTypeInspector");		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Inspecting the object of class %2$s: %1$s", toInspect, toInspect != null? toInspect.getClass().getName(): ""));
		}
		// config based on validating inspector
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig();
		inspectorConfig.setInspectors(new PropertyTypeInspector());
		// create inspector
		CompositeInspector inspector = new CompositeInspector (inspectorConfig);
		
		// inspect 
		String result = inspector.inspect(toInspect, type);
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Inspected: %1$s", result));
		}
		if (toInspect != null) {
			Assert.assertNotNull (result, "The inspected result shall be provided");
		}
	}	

	@Test (dataProvider="validValuesForInspector")
	public void testViewDefinitionRuleOutcomeNotRequired(DomainObject toInspect, String type) throws Exception {
		// start marker
		logStartTestMarker ("testViewDefinitionRuleOutcomeNotRequired");
		
		// initial state for view definition
		ViewDefinition vd = new ViewDefinition (toInspect, "exit");
		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Testing the view definition rule - outcome not required for views other than 'main' - the domain object of class %2$s: %1$s", 
					toInspect, toInspect != null? toInspect.getClass().getName(): ""));
			
			log.debug(String.format("View Definition - initial state: %1$s", vd));
		}
		
		//
		// create and execute business rules without retraction
		//
		// create rule base
		RuleBase rb = PackageFactory.createRuleBase(new Object[]{UI_RULES_SRC});
		// check that rule base actually created
		Assert.assertNotNull (rb, "Rule Base shall be created");
		// execute rules
		WorkingMemory wm = PackageFactory.executeRules (rb, null, false, new Object[]{vd});
		// check
		Assert.assertNotNull (wm, "A working memory shall be created by Package Factory");
		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Tested the view definition rule - outcome not required for views other than 'main' - the domain object of class %2$s: %1$s", 
					toInspect, toInspect != null? toInspect.getClass().getName(): ""));
			
			log.debug(String.format("View Definition - result: %1$s", vd));
		}
		// check results 
		Assert.assertNotNull (vd.getDomainObject(), "The view definition shall have the domain object");
		Assert.assertNotNull (vd.getPropertyAttribute("outcomeWeight", "read-only"), "The 'outcomeWeight' property shall have the following attribute: 'read-only'");
		Assert.assertNotNull (vd.getPropertyAttribute("outcomeSet", "hidden"), "The 'outcomeSet' property shall have the following attribute: 'hidden'");
		Assert.assertNotNull (vd.getPropertyAttribute("outcome", "label"), "The 'outcome' property shall have the following attribute: 'label'");
		Assert.assertNull (vd.getPropertyAttribute("outcome", "required"), "The 'outcome' property shall NOT have the following attribute: 'required'");
	}	

	@Test (dataProvider="validValuesForInspector")
	public void testViewDefinitionRuleOutcomeRequired(DomainObject toInspect, String type) throws Exception {
		// start marker
		logStartTestMarker ("testViewDefinitionRuleOutcomeRequired");
		
		// initial state for view definition
		ViewDefinition vd = new ViewDefinition (toInspect, "main");
		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Testing the view definition rule - outcome not required for views other than 'main' - the domain object of class %2$s: %1$s", 
					toInspect, toInspect != null? toInspect.getClass().getName(): ""));
			
			log.debug(String.format("View Definition - initial state: %1$s", vd));
		}
		
		//
		// create and execute business rules without retraction
		//
		// create rule base
		RuleBase rb = PackageFactory.createRuleBase(new Object[]{UI_RULES_SRC});
		// check that rule base actually created
		Assert.assertNotNull (rb, "Rule Base shall be created");
		// execute rules
		WorkingMemory wm = PackageFactory.executeRules (rb, null, false, new Object[]{vd});
		// check
		Assert.assertNotNull (wm, "A working memory shall be created by Package Factory");
		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Tested the view definition rule - outcome not required for views other than 'main' - the domain object of class %2$s: %1$s", 
					toInspect, toInspect != null? toInspect.getClass().getName(): ""));
			
			log.debug(String.format("View Definition - result: %1$s", vd));
		}
		// check results 
		Assert.assertNotNull (vd.getDomainObject(), "The view definition shall have the domain object");
		Assert.assertNotNull (vd.getPropertyAttribute("outcomeWeight", "read-only"), "The 'outcomeWeight' property shall have the following attribute: 'read-only'");
		Assert.assertNotNull (vd.getPropertyAttribute("outcomeSet", "hidden"), "The 'outcomeSet' property shall have the following attribute: 'hidden'");
		Assert.assertNotNull (vd.getPropertyAttribute("outcome", "label"), "The 'outcome' property shall have the following attribute: 'label'");
		Assert.assertNotNull (vd.getPropertyAttribute("outcome", "required"), "The 'outcome' property shall have the following attribute: 'required'");
	}
	
	@Test (dataProvider="validValuesForInspector")
	public void testBusinessRulesInspector(Object toInspect, String type) throws Exception {
		// start marker
		logStartTestMarker ("testBusinessRulesInspector");		
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Inspecting the object of class %2$s: %1$s", toInspect, toInspect != null? toInspect.getClass().getName(): ""));
		}
		// config based on validating inspector
		CompositeInspectorConfig inspectorConfig = new CompositeInspectorConfig();
		inspectorConfig.setInspectors(new BusinessRulesInspector(new BusinessRulesInspectorConfig (UI_RULES_SRC)));
		// create inspector
		CompositeInspector inspector = new CompositeInspector (inspectorConfig);
		
		// inspect 
		String result = inspector.inspect(toInspect, type);
		// debug
		if (log.isDebugEnabled()) {
			log.debug(String.format("Inspected: %1$s", result));
		}
		if (toInspect != null) {
			Assert.assertNotNull (result, "The inspected result shall be provided");
		}
	}	
	
}
